List interaction
This example demonstrates how to implement interactive list items in the Scripting app using swipe gestures. By leveraging leadingSwipeActions
and trailingSwipeActions
, you can provide contextual actions such as marking a message as unread, deleting a message, or flagging it.
Overview
You will learn how to:
- Display a list of messages using a custom cell layout
- Implement swipe actions on both leading and trailing edges
- Configure swipe behavior (e.g. disabling full swipe)
- Use
Button
, Label
, and Circle
for interactive UI elements
Example Code
1. Define Message Data Type
1type Message = {
2 from: string
3 content: string
4 isUnread: boolean
5}
2. Create a Custom Message Cell
Each message is rendered with a colored indicator (for unread status), sender name, and content using HStack
and VStack
.
1function MessageCell({
2 message
3}: {
4 message: Message
5}) {
6 return <HStack>
7 <Circle
8 fill={message.isUnread ? "systemBlue" : "clear"}
9 frame={{
10 width: 16,
11 height: 16,
12 }}
13 />
14 <VStack alignment={"leading"}>
15 <Text font={"headline"}>{message.from}</Text>
16 <Text>{message.content}</Text>
17 </VStack>
18 </HStack>
19}
3. Manage State and Actions
1const [messages, setMessages] = useState<Message[]>(...)
2
3function toggleUnread(message: Message) {
4 setMessages(messages.map(item =>
5 item !== message ? item : { ...message, isUnread: !item.isUnread }
6 ))
7}
8
9function deleteMessage(message: Message) {
10 setMessages(messages.filter(item => item !== message))
11}
4. Construct the List with Swipe Actions
1return <NavigationStack>
2 <List
3 navigationTitle={"Messages"}
4 navigationBarTitleDisplayMode={"inline"}
5 listStyle={"inset"}
6 >
7 {messages.map(message =>
8 <MessageCell
9 message={message}
10 leadingSwipeActions={{
11 allowsFullSwipe: false,
12 actions: [
13 <Button
14 action={() => toggleUnread(message)}
15 tint={"systemBlue"}
16 >
17 {message.isUnread
18 ? <Label title={"Read"} systemImage={"envelope.open"} />
19 : <Label title={"Unread"} systemImage={"envelope.badge"} />
20 }
21 </Button>
22 ]
23 }}
24 trailingSwipeActions={{
25 actions: [
26 <Button
27 role={"destructive"}
28 action={() => deleteMessage(message)}
29 >
30 <Label title={"Delete"} systemImage={"trash"} />
31 </Button>,
32 <Button
33 action={() => {}}
34 tint={"systemOrange"}
35 >
36 <Label title={"Flag"} systemImage={"flag"} />
37 </Button>
38 ]
39 }}
40 />
41 )}
42 </List>
43</NavigationStack>
5. Present the View and Exit
1async function run() {
2 await Navigation.present({
3 element: <Example />
4 })
5
6 Script.exit()
7}
8
9run()
Key Features
- leadingSwipeActions: Add actions triggered by swiping from the leading edge (left-to-right in LTR layouts).
- trailingSwipeActions: Add actions triggered by swiping from the trailing edge.
- allowsFullSwipe: When set to
false
, prevents full swipe from automatically triggering the first action.
- Button Roles: Use roles like
"destructive"
to style buttons (e.g., red for delete).
- tint: Customize button color for better visual context.
Use Cases
- Email/Messaging Scripts: Mark messages as read/unread, delete, archive, or flag.
- To-Do Lists: Complete or remove tasks with quick gestures.
- Custom Tools: Attach context-specific actions to list items.
Swipe actions provide an efficient and intuitive way for users to perform actions directly within list views, improving interaction speed and user experience.